#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include "log.hpp"

enum Domain
{
    IPV4 = AF_INET,
    IPV6 = AF_INET6
};
enum SocketType
{
    TCP,
    UDP

};

class SocketInfo
{
public:
    SocketInfo(const int &socketfd, const uint16_t &port, const std::string &ip) : socketfd_(socketfd),
                                                                                   port_(port),
                                                                                   ip_(ip)
    {
    }

    SocketInfo()
    {
    }
    int socketfd_;
    uint16_t port_;
    std::string ip_;
};
class Socket
{
private:
    static const uint16_t deafault_port;
    static const std::string deafault_ip;
    static const int deafault_backlog;
    enum ExitCode
    {
        SOCKET_ERROR,
        BIND_ERROR,
        LISTEN_ERROE

    };
    /// @brief 打印fatal
    /// @param func_name
    inline void LgError(const char *func_name)
    {
        lg.logmessage(Fatal, "%s errno:%s", func_name, strerror(errno));
    }

public:
    Socket(Domain domain = Domain::IPV4, SocketType socket_type = SocketType::TCP) : domain_(domain)
    {
        socket_type_ = socket_type;
    }
    const int &ReturnSocketfd()
    {
        return socketfd_;
    }
    /// @brief 创建一个通信
    void CreatSocket()
    {
        switch (socket_type_)
        {
        case TCP:
        {
            socketfd_ = socket(domain_, SOCK_STREAM, 0);
            if (socketfd_ < 0)
            {
                LgError("socket");
                exit(SOCKET_ERROR);
            }
            lg.logmessage(Info, "socket successed");
        }
        break;
        default:
            break;
        }
        int opt = 1;
        setsockopt(socketfd_, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));
    }
    /// @brief 绑定一个接口
    /// @param port 接口
    /// @param ip 地址
    void Bind(uint16_t port = Socket::deafault_port, const std::string &ip = Socket::deafault_ip)
    {
        sockaddr_in in;
        socklen_t len = sizeof(sockaddr_in);
        memset(&in, 0, len);
        inet_aton(ip.c_str(), &in.sin_addr);
        in.sin_family = domain_;
        in.sin_port = htons(port);
        int ret = bind(socketfd_, (sockaddr *)(&in), len);
        if (ret < 0)
        {
            LgError("bind");
            exit(BIND_ERROR);
        }
        lg.logmessage(Info, "bind successed");
    }

    void Listen(int backlog = Socket::deafault_backlog)
    {
        int ret = listen(socketfd_, backlog);
        if (ret < 0)
        {
            LgError("listen");
            exit(LISTEN_ERROE);
        }
        lg.logmessage(Info, "listen successed");
    }

    bool Accept(SocketInfo &info)
    {
        sockaddr_in in;
        socklen_t len = 0;
        memset(&in, 0, sizeof(sockaddr_in));
        int clinet_socketfd = accept(socketfd_, (sockaddr *)&in, &len);
        if (clinet_socketfd < 0)
        {
            LgError("accept");
            return false;
        }
        info.socketfd_ = clinet_socketfd;
        info.ip_ = inet_ntoa(in.sin_addr);
        info.port_ = ntohs(in.sin_port);
        lg.logmessage(Info, "add a new client %s:%d", info.ip_.c_str(), info.port_);
        return true;
    }

    int Send(int socketfd_, const std::string &in_str)
    {
        lg.logmessage(Debug, "send message:%s\n send size:%d", in_str.c_str(), in_str.size());
        int ret = write(socketfd_, in_str.c_str(), in_str.size());
        if (ret <= 0)
        {
            lg.logmessage(Warning, "write ret:%d errno:%d strerrno:%s", ret, errno, strerror(errno));
        }
        return ret;
    }
    /// @brief 会将out_str里的内容全覆盖
    /// @param socketfd
    /// @param out_str
    /// @return
    int Recive(int socketfd, std::string &out_str)
    {
        char buffer[2048] = {0};
        int ret = read(socketfd, buffer, 2048);
        out_str = buffer;
        if (ret <= 0)
        {
            lg.logmessage(Warning, "read ret:%d errno:%d strerrno:%s", ret, errno, strerror(errno));
        }
        lg.logmessage(Debug, "recived message:%s\n read size:%d", out_str.c_str(), out_str.size());
        return ret;
    }

    bool Connect(const uint16_t &port, const std::string &ip)
    {
        sockaddr_in in;
        socklen_t len = sizeof(sockaddr_in);
        memset(&in, 0, len);
        inet_aton(ip.c_str(), &in.sin_addr);
        in.sin_family = domain_;
        in.sin_port = htons(port);
        int ret = connect(socketfd_, (sockaddr *)&in, len);
        if (ret < 0)
        {
            LgError("connect");
            return false;
        }
        else
        {
            lg.logmessage(Info, "connect successed %s:%d", ip.c_str(), port);
        }
        return true;
    }

private:
    int socketfd_;
    int domain_;
    SocketType socket_type_;
};

const uint16_t Socket::deafault_port = 8080;
const std::string Socket::deafault_ip("0.0.0.0");
const int Socket::deafault_backlog = 10;
